package com.example.servicedemo;

import java.util.ArrayList;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class CycleCoachServiceActivityDemo extends Activity{
private static final String TAG = "CycleCoachServiceActivityDemo";
	
	Button mHrmButton;
	Button mCscButton;
	
	TextView mHrmName;
	TextView mHrmStatus;
	TextView mHrmBPM;
	TextView mCscName;
	TextView mCscStatus;
	TextView mCscSpeed;
	TextView mCscCadence;
	
	
	private final String HRM_ADDRESS = "F5:3A:70:C6:CB:17";
	private final String CSC_ADDRESS = "DC:6B:67:04:21:93";
	
	private Activity mParent;
	private ArrayList<BtleDeviceTracker> mDevices;
	private int connectIterator;
	private boolean shutdownInProgress = false;
	
	@Override
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_btledevicedemo);
		mParent = this;
		
		mHrmName = (TextView)findViewById(R.id.hrmName);
		mHrmStatus = (TextView)findViewById(R.id.hrmStatus);
		mHrmBPM = (TextView)findViewById(R.id.hrmBPM);
		mHrmButton = (Button)findViewById(R.id.hrmButton);
		mHrmButton.setEnabled(false);
		/*mHrmButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if(mHrmButton.getText().equals("Start")) {
					// Send message to start notifications from HRM device
				}
				else {
					// Send message to stop notifications on HRM device
					mHrmButton.setText("Start");				
					mHrmName.setText("N/A");
					mHrmBPM.setText("N/A");
					mHrmButton.setEnabled(false);
				}
			}
			
		});*/
		
		mCscName = (TextView)findViewById(R.id.cscName);
		mCscStatus = (TextView)findViewById(R.id.cscStatus);
		mCscSpeed = (TextView)findViewById(R.id.cscSpeed);
		mCscCadence = (TextView)findViewById(R.id.cscCadence);
		mCscButton = (Button)findViewById(R.id.cscButton);
		mCscButton.setEnabled(false);
		/*mCscButton.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				if(mCscButton.getText().equals("Start")) {
					// Send message to connect to CSC device
					
				} else {
					// Send message to stop notifications on CSC device
					
					mCscButton.setText("Start");				
					mCscName.setText("N/A");
					mCscSpeed.setText("N/A");
					mCscCadence.setText("N/A");
					mCscButton.setEnabled(false);
				}
					
			}
		});*/
		
		
	}

	
	@Override
	public void onPause() {
		super.onPause();
		
		// If no sensors are currently connected we need to shutdown the
		// CycleCoach service.
		doUnbindService();
	}
	
	@Override
	public void onResume() {
		super.onResume();
		
		// Startup the CycleCoach service so we can connect to paired devices
		doBindService();
		
		sendMsgToService(CycleCoachService.MSG_REGISTER_CLIENT, 0, 0, null);
	}
	
	private void onRegistrationAccepted() {
		Log.d(TAG, "(onRegistrationAccepted) Registered with CycleCoach service.");
		mDevices = new ArrayList<BtleDeviceTracker>();
		mDevices.add(new BtleDeviceTracker(HRM_ADDRESS, BtleDeviceType.HEART_RATE_MONITOR));
		mDevices.add(new BtleDeviceTracker(CSC_ADDRESS, BtleDeviceType.CYCLING_SPEED_CADENCE_SENSOR));
		connectIterator = 0;
		connectNextDevice();
	}
	
	private void connectNextDevice() {
		
		if(shutdownInProgress) return;
		
		BtleDeviceTracker tracker;
		int count = mDevices.size();
		Log.d(TAG, "(connectNextDevice) start.");
			
		while((tracker = mDevices.get(connectIterator)).mIsConnected == true) {
			connectIterator = (connectIterator + 1) % mDevices.size();
			if(--count == 0) {
				Log.d(TAG, "All devices connected.");
				return;
			}
		}
		
		connectIterator = (connectIterator + 1) % mDevices.size();
		Bundle bundle = new Bundle();
		bundle.putString(CycleCoachService.EXTRA_BTLE_ADDRESS, tracker.mAddress);
		bundle.putInt(CycleCoachService.EXTRA_BTLE_TYPE, tracker.mType.getValue());
		sendMsgToService(CycleCoachService.MSG_CONNECT_BTLE_DEVICE, 0, 0, bundle);
		return;
	}
	
	private boolean sendMsgToService(int command, int arg1, int arg2, Bundle bundle) {
		try {
			if(mService != null) {
				Message msg = Message.obtain(null, command, arg1, arg2);
				if(bundle != null) {
					msg.setData(bundle);
				}
				mService.send(msg);
				return true;
			}
		} catch (RemoteException e) {
			serviceConnectionLost();
			return false;
		}
		
		return false;
	}
	
	public void serviceConnectionLost() {
		
	}
	
	public void updateHrmTextView(final long timestamp, final int bpm) {
		mParent.runOnUiThread(new Runnable() {

			@Override
			public void run() {
				mHrmBPM.setText(bpm + " BPM");
			}
		});
		
	}
	
	public void updateCscTextView(
			final double speedKPH, final int crankRPM,
				final int wheelTime, final int crankTime) {
		
		mParent.runOnUiThread(new Runnable() {

			@Override
			public void run() {
				mCscSpeed.setText(String.format("%1.2f KPH", speedKPH));				
				mCscCadence.setText(crankRPM + " RPM");
			}
		});
	}
	
	private class BtleDeviceTracker {
		boolean mIsConnected;
		String mAddress;
		BtleDeviceType mType;
		
		BtleDeviceTracker(String addr, BtleDeviceType type) {
			mAddress = addr;
			mType = type;
			mIsConnected = false;
		}
	}
	
	private BtleDeviceTracker findTracker(String address) {
		for(BtleDeviceTracker a: mDevices) {
			if(a.mAddress.equals(address)) return a;
		}
		
		return null;
	}
	
	private void resetGUI() {
		mParent.runOnUiThread(new Runnable() {
        	
			@Override
			public void run() {
				mHrmButton.setEnabled(false);
				mHrmButton.setText("Start");
				mHrmName.setText("N/A");
				mCscButton.setText("Start");
				mCscButton.setEnabled(false);
				mCscName.setText("N/A");
				
				mHrmName.setText("N/A");
				mHrmBPM.setText("N/A");
				mCscSpeed.setText("N/A");
				mCscCadence.setText("N/A");
			}
			
		});
	}
	
	
	
	
	/***********************************************************************
	 ***********************************************************************
     * CycleCoach service communication protocols
     ***********************************************************************
     ***********************************************************************/
	
	/** Messenger for communicating with service. */
	Messenger mService = null;
	
	/** Flag indicating whether we have called bind on the service. */
	boolean mIsBound;
	
	class IncomingHandler extends Handler {
		@Override
		public void handleMessage(Message msg) {
			Message reply;
			Bundle data = (Bundle) msg.getData();
			Bundle bundle = new Bundle();
			String address;
			BtleDeviceTracker tracker;
			Log.d(TAG, "(Activity - handleMessage) " + msg.what);
			switch(msg.what) {
			case MSG_REGISTRATION_ACCEPTED:
				onRegistrationAccepted();
				break;
			case MSG_REGISTRATION_DENIED:
				Log.d(TAG, "Server refused connection request.");
				// Detach our existing connection.
		        unbindService(mConnection);
		        mIsBound = false;
		        Toast.makeText(CycleCoachServiceActivityDemo.this, "CycleCoach service refused connection.",
		                Toast.LENGTH_SHORT).show();
				break;
				
			case MSG_DEREGISTER_ACK:
				Log.d(TAG, "No longer registered with CycleCoach server process.");
				// Detach our existing connection.
		        unbindService(mConnection);
		        mIsBound = false;
		        Toast.makeText(CycleCoachServiceActivityDemo.this, "Unbinding CycleCoach service",
		                Toast.LENGTH_SHORT).show();
		        resetGUI();
				break;
				
			case MSG_BTLE_CONNECT_SUCCESS:
				address = data.getString(CycleCoachService.EXTRA_BTLE_ADDRESS, null);
				tracker = findTracker(address);
				
				if(tracker == null) {
					Log.d(TAG, "(handleMessage) findTracker() returned null.");
					return;
				}
				
				final String devName = data.getString(CycleCoachService.EXTRA_BTLE_DEVICE_NAME);
				if(tracker.mType == BtleDeviceType.HEART_RATE_MONITOR) {
					mParent.runOnUiThread(new Runnable() {
	
						@Override
						public void run() {
							//mHrmButton.setEnabled(true);
							mHrmName.setText(devName);
							mHrmStatus.setText("Online");
						}
						
					});
					
					
					bundle.putString(CycleCoachService.EXTRA_BTLE_ADDRESS, tracker.mAddress);
					bundle.putInt(CycleCoachService.EXTRA_BTLE_TYPE, tracker.mType.getValue());
					
				} else if(tracker.mType == BtleDeviceType.CYCLING_SPEED_CADENCE_SENSOR) {
					mParent.runOnUiThread(new Runnable() {
	
						@Override
						public void run() {
							//mCscButton.setEnabled(true);
							mCscName.setText(devName);
							mCscStatus.setText("Online");
						}
						
					});
					
					bundle.putString(CycleCoachService.EXTRA_BTLE_ADDRESS, tracker.mAddress);
					bundle.putInt(CycleCoachService.EXTRA_BTLE_TYPE, tracker.mType.getValue());
					bundle.putDouble(CycleCoachService.EXTRA_WHEEL_CIRCUMFERENCE, 2.096);
				}
				
				sendMsgToService(CycleCoachService.MSG_BTLE_START_CLIENT_DATA_UPDATES, 0, 0, bundle);
				
				Log.d(TAG, String.format("BtleDevice %s connected", address));
				tracker.mIsConnected = true;
				connectNextDevice();
				break;
				
			case MSG_BTLE_CONNECT_FAILED:
				Log.d(TAG, String.format("BtleDevice %s connection error: %s", 
						data.getString(CycleCoachService.EXTRA_BTLE_ERROR_STRING, "Unknown error")));
				connectNextDevice();
				break;
				
			case MSG_BTLE_CONNECTION_DROPPED:
				break;
			case MSG_BTLE_DATA_UPDATE:
				address = data.getString(CycleCoachService.EXTRA_BTLE_ADDRESS, null);
				tracker = findTracker(address);
				Log.d(TAG, "MSG_BTLE_DATA_UPDATE: " + address);
				if(tracker.mType == BtleDeviceType.HEART_RATE_MONITOR) {
					final int bpm = data.getInt(CycleCoachService.EXTRA_HRM_BPM);
					mParent.runOnUiThread(new Runnable() {
	
						@Override
						public void run() {
							mHrmBPM.setText(String.format("%d BPM", bpm));
						}
						
					});
				} else if(tracker.mType == BtleDeviceType.CYCLING_SPEED_CADENCE_SENSOR) {
					final double speed = data.getDouble(CycleCoachService.EXTRA_CSC_SPEED);
					final int rpm = data.getInt(CycleCoachService.EXTRA_CSC_CRANK_RPM);
					mParent.runOnUiThread(new Runnable() {
						
						@Override
						public void run() {
							mCscSpeed.setText(String.format("%1.2f KPH", speed));
							mCscCadence.setText(String.format("%d RPM", rpm));
						}
					});
				}
				break;
			case MSG_BTLE_DEVICE_START_SESSION_ACK:
				break;
			case MSG_BTLE_DEVICE_STOP_SESSION_ACK:
				break;
			case MSG_BTLE_DEVICE_PAUSE_SESSION_ACK:
				break;
			case MSG_BTLE_DEVICE_RESUME_SESSION_ACK:
				break;
			case MSG_BTLE_DEVICE_INCREMENT_LAP_SESSION_ACK:
				break;
			case MSG_BTLE_DEVICE_NOT_FOUND:
				break;
			}
		}
	}
	
	/***********************************************************************
     * List of message types that can be sent from CycleCoach server.
     ***********************************************************************/
	public static final int MSG_REGISTRATION_ACCEPTED = 1;
	public static final int MSG_REGISTRATION_DENIED = 2;
	public static final int MSG_DEREGISTER_ACK = 3;
	public static final int MSG_BTLE_CONNECT_SUCCESS = 4;
	public static final int MSG_BTLE_CONNECT_FAILED = 5;
	public static final int MSG_BTLE_CONNECTION_DROPPED = 6;
	public static final int MSG_BTLE_DATA_UPDATE = 7;
	public static final int MSG_BTLE_DEVICE_START_SESSION_ACK = 8;
	public static final int MSG_BTLE_DEVICE_STOP_SESSION_ACK = 9;
	public static final int MSG_BTLE_DEVICE_PAUSE_SESSION_ACK = 10;
	public static final int MSG_BTLE_DEVICE_RESUME_SESSION_ACK = 11;
	public static final int MSG_BTLE_DEVICE_INCREMENT_LAP_SESSION_ACK = 12;
	public static final int MSG_BTLE_DEVICE_NOT_FOUND = 13;
	
	/**
	 * Target we publish for clients to send messages to IncomingHandler.
	 */
	final Messenger mMessenger = new Messenger(new IncomingHandler());
	
	/**
	 * Class for interacting with the main interface of the service.
	 */
	private ServiceConnection mConnection = new ServiceConnection() {
	    public void onServiceConnected(ComponentName className,
	            IBinder service) {
	        // This is called when the connection with the service has been
	        // established, giving us the service object we can use to
	        // interact with the service.  We are communicating with our
	        // service through an IDL interface, so get a client-side
	        // representation of that from the raw service object.
	        mService = new Messenger(service);
	        //mCallbackText.setText("Attached.");

	        // We want to monitor the service for as long as we are
	        // connected to it.
	        try {
	            Message msg = Message.obtain(null,
	                    CycleCoachService.MSG_REGISTER_CLIENT);
	            msg.replyTo = mMessenger;
	            mService.send(msg);
	        } catch (RemoteException e) {
	            // In this case the service has crashed before we could even
	            // do anything with it; we can count on soon being
	            // disconnected (and then reconnected if it can be restarted)
	            // so there is no need to do anything here.
	        	Toast.makeText(CycleCoachServiceActivityDemo.this, "Unable to connect to CycleCoach service.",
	                    Toast.LENGTH_SHORT).show();
	        }

	        // Inform the user we successfully connected to the service.
	        Toast.makeText(CycleCoachServiceActivityDemo.this, "CycleCoach service connected",
	                Toast.LENGTH_SHORT).show();
	    }

	    public void onServiceDisconnected(ComponentName className) {
	        // This is called when the connection with the service has been
	        // unexpectedly disconnected -- that is, its process crashed.
	        mService = null;

	        // As part of the sample, tell the user what happened.
	        Toast.makeText(CycleCoachServiceActivityDemo.this, "CycleCoach service disconnected",
	                Toast.LENGTH_SHORT).show();
	    }
	};

	void doBindService() {
	    // Establish a connection with the service.
	    bindService(new Intent(CycleCoachServiceActivityDemo.this, 
	            CycleCoachService.class), mConnection, Context.BIND_AUTO_CREATE);
	    mIsBound = true;
	    Toast.makeText(CycleCoachServiceActivityDemo.this, "Binding to CycleCoach service",
                Toast.LENGTH_SHORT).show();
	}

	void doUnbindService() {
		shutdownInProgress = true;
	    if (mIsBound) {
	        // If we have received the service, and hence registered with
	        // it, then now is the time to unregister.
	        if (mService != null) {
	            try {
	                Message msg = Message.obtain(null,
	                        CycleCoachService.MSG_DEREGISTER_CLIENT);
	                msg.replyTo = mMessenger;
	                mService.send(msg);
	            } catch (RemoteException e) {
	                // There is nothing special we need to do if the service
	                // has crashed.
	            	unbindService(mConnection);
	    	        mIsBound = false;
	    	        Toast.makeText(CycleCoachServiceActivityDemo.this, "Unbinding CycleCoach service",
	    	                Toast.LENGTH_SHORT).show();
	            }
	        }
	    }
	}
	
	
	

}
